﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace Network
{
    public class ServerManager
    {
        public int Port { get; set; }
        public TcpListener Listener;
        public List<TcpClient> Clients;
        public List<NetworkStream> NetworkStream;
        public delegate void ReceiveDataEventHandler(int clientid, byte[] data);
        public delegate void JoinEventHandler(int clientid);
        public event ReceiveDataEventHandler OnReceiveData;
        public event JoinEventHandler OnClientJoin;

        public ServerManager(int port)
        {
            try
            {
                Port = port;
                Clients = new List<TcpClient>();
                NetworkStream = new List<NetworkStream>();
                Listener = new TcpListener(new IPEndPoint(IPAddress.Any, Port));
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: " + ex.ToString());
            }
        }

        public void Start()
        {
            try
            {
                Listener.Start();
                Console.WriteLine("SERVER: Server started on port " + Port.ToString());
                Listener.BeginAcceptTcpClient(new AsyncCallback(ClientConnected), null);
                Console.WriteLine("SERVER: Server accepting connections");
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: " + ex.ToString());
            }
        }

        public void Stop()
        {
            try
            {
                Listener.Stop();

                for (int i = 0; i < Clients.Count; i++)
                {
                    Clients[i].Close();
                    NetworkStream[i].Close();
                }

                Console.WriteLine("SERVER: Server stopped");
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: " + ex.ToString());
            }

        }

        public void SendMessageToClient(int clientid, string message)
        {
            int clientidx = clientid - 1;
            PacketWriter packetwriter = new PacketWriter();
            packetwriter.Write((ushort)Headers.Text);
            packetwriter.Write(message);
            byte[] databuffer = packetwriter.GetBytes();
            NetworkStream[clientidx].Write(databuffer, 0, databuffer.Length);
            NetworkStream[clientidx].Flush();
        }

        public void SendMessageToAllClient(string message)
        {
            for (int i = 1; i <= Clients.Count; i++)
            {
                SendMessageToClient(i, message);
            }
        }

        public void SendDataToAllClientExcept(int clientid, byte[] data)
        {
            for (int i = 1; i <= Clients.Count; i++)
            {
                if (clientid != i)
                {
                    int clientidx = i - 1;
                    NetworkStream[clientidx].Write(data, 0, data.Length);
                    NetworkStream[clientidx].Flush();
                }
            }
        }

        public void SendDataToAllClient(byte[] data)
        {
            for (int i = 1; i <= Clients.Count; i++)
            {
                int clientidx = i - 1;
                NetworkStream[clientidx].Write(data, 0, data.Length);
                NetworkStream[clientidx].Flush();
            }
        }

        private void ClientConnected(IAsyncResult ar)
        {
            try
            {
                TcpClient client = Listener.EndAcceptTcpClient(ar);
                IPEndPoint endpoint = client.Client.RemoteEndPoint as IPEndPoint;
                Clients.Add(client);
                NetworkStream.Add(client.GetStream());

                if (OnClientJoin != null) OnClientJoin(Clients.Count);

                Console.WriteLine("SERVER: Client connected " + endpoint.Address.ToString() + " with ID " + Clients.Count);
                Thread thrdReceive = new Thread(new ParameterizedThreadStart(ReceiveClientData));
                thrdReceive.Start(Clients.Count);
                Listener.BeginAcceptTcpClient(new AsyncCallback(ClientConnected), null);
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: " + ex.ToString());
            }
        }

        private void ReceiveClientData(object client)
        {
            try
            {
                int clientid = (int)client;
                int clientidx = clientid - 1;
                int buffersize;
                byte[] databuffer;

                while (true)
                {
                    databuffer = new byte[4086];
                    buffersize = NetworkStream[clientidx].Read(databuffer, 0, databuffer.Length);
                    Array.Resize(ref databuffer, buffersize);

                    if (OnReceiveData != null) OnReceiveData(clientid, databuffer);

                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: " + ex.ToString());
            }
        }
    }
}
